<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Scrcpy-WebUI</title>
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="referrer" content="no-referrer">
    <link rel="shortcut icon" href="icons/icon.png" type="image/png">
    <link rel="stylesheet" href="css/mdui.min.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/jquery.min.js"></script>
    <script src="js/mdui.min.js"></script>
</head>
<body>
    <div class="header">
        <mdui-top-app-bar scroll-behavior="hide" scroll-threshold="30" scroll-target=".example-scroll-behavior-hide">
            <img src="icons/icon.png" class="nat-icon">
            <mdui-top-app-bar-title>Scrcpy-WebUI</mdui-top-app-bar-title>
            <div style="flex-grow: 1"></div>
            <mdui-tooltip content="切换主题">
                <mdui-button-icon icon="wb_sunny" class="theme" onclick="extendWebPage.toggleTheme()"></mdui-button-icon>
            </mdui-tooltip>
        </mdui-top-app-bar>
        <div class="example-scroll-behavior-hide" style="overflow: auto;"></div>
    </div>
    <div class="main">
        <div class="devices">
            <div class="bx">
                <h3 class="title"><i>设备列表</i></h3>
                <mdui-checkbox class="auto-start">自动启动scrcpy</mdui-checkbox>
                <mdui-tooltip content="重启ADB服务">
                    <mdui-button class="reboot-server" variant="tonal">重启服务</mdui-button>
                </mdui-tooltip>
                <mdui-divider></mdui-divider>
                <mdui-list class="device-list">
                </mdui-list>
            </div>
        </div>
        <div class="options">
            <div class="bx">
                <h3 class="title"><i>启动选项</i></h3><mdui-divider></mdui-divider>
                <div class="center">
                    <span style="margin-left: 10px;width: 52px">码率: </span><mdui-slider class="video-bit-rate" data-key="videoBitRate" min="1" max="32"></mdui-slider>
                </div>
                <div class="center">
                    <mdui-select class="mr max-fps" data-key="maxFps" label="最大帧率" value="off">
                        <mdui-menu-item value="off">不限制</mdui-menu-item>
                        <mdui-menu-item value="15">15FPS</mdui-menu-item>
                        <mdui-menu-item value="30">30FPS</mdui-menu-item>
                        <mdui-menu-item value="60">60FPS</mdui-menu-item>
                        <mdui-menu-item value="120">120FPS</mdui-menu-item>
                    </mdui-select>
                    <mdui-select class="mr max-size" data-key="maxSize" label="最大尺寸" value="off">
                        <mdui-menu-item value="640">640</mdui-menu-item>
                        <mdui-menu-item value="720">720</mdui-menu-item>
                        <mdui-menu-item value="1080">1080</mdui-menu-item>
                        <mdui-menu-item value="1280">1280</mdui-menu-item>
                        <mdui-menu-item value="1920">1920</mdui-menu-item>
                        <mdui-menu-item value="off">原始</mdui-menu-item>
                    </mdui-select>
                    <mdui-select class="mr display-orientation" data-key="displayOrientation" label="显示方向" value="off">
                        <mdui-menu-item value="off">默认</mdui-menu-item>
                        <mdui-menu-item value="0">0°</mdui-menu-item>
                        <mdui-menu-item value="90">90°</mdui-menu-item>
                        <mdui-menu-item value="180">180°</mdui-menu-item>
                        <mdui-menu-item value="270">270°</mdui-menu-item>
                    </mdui-select>
                </div>
                <div class="start-option">
                    <div class="checkbox">
                        <mdui-checkbox data-key="noDisplay">后台录制</mdui-checkbox>
                        <mdui-checkbox data-key="alwaysOnTop">窗口置顶</mdui-checkbox>
                        <mdui-checkbox data-key="turnScreenOff">自动息屏</mdui-checkbox>
                        <mdui-checkbox data-key="windowBorderless">无边框</mdui-checkbox>
                        <mdui-checkbox data-key="fullScreen">全屏</mdui-checkbox>
                        <mdui-checkbox data-key="stayAwake">保持唤醒</mdui-checkbox>
                        <mdui-checkbox data-key="noAudio">关闭音频</mdui-checkbox>
                        <mdui-checkbox data-key="noVideo">关闭视频</mdui-checkbox>
                    </div>
                </div>
            </div>
            <div class="bx">
                <h3 class="title"><i>连接设置</i></h3><mdui-divider></mdui-divider>
                <mdui-tabs value="usb" full-width>
                    <mdui-tab value="usb">USB数据线</mdui-tab>
                    <mdui-tab value="wireless">无线连接</mdui-tab>
                    <mdui-tab-panel slot="panel" value="usb">Panel 1</mdui-tab-panel>
                    <mdui-tab-panel slot="panel" value="wireless">Panel 2</mdui-tab-panel>
                </mdui-tabs>
            </div>
        </div>
    </div>
    <mdui-snackbar class="snackbar" auto-close-delay="2800">archived</mdui-snackbar>
</body>
<script>
    (function (global) {
        let routeLocal = {
            routes: '*://localhost:5352/*',
            extend: {
                $header: $('body > .header'),
                $snackbar: $('body > .snackbar'),
                $deviveList: $('.main .device-list'),
                $autoStart: $('.main .devices .auto-start'),
                /**
                 * toggleTheme 切换主题
                 * @returns {void}
                */
                toggleTheme() {
                    localStorage.theme = mdui.getTheme() === 'light' ? 'dark' : 'light'
                    mdui.setTheme(localStorage.theme)
                    $('mdui-button-icon.theme').attr('icon', localStorage.theme === 'light' ? 'wb_sunny' : 'bedtime')
                },
                /**
                 * deepEqual 深度检测两个对象是否相同
                 * @param {Object} obj1 对象1
                 * @param {Object} obj2 对象2
                 * @returns {Boolean} 相同返回 true, 不同返回 false
                */
                deepEqual(obj1, obj2) {
                    if (obj1 === obj2) return true
                    if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null)
                        return false
                    const keys1 = Object.keys(obj1)
                    const keys2 = Object.keys(obj2)
                    if (keys1.length !== keys2.length) return false
                    for (let key of keys1) {
                        if (!this.deepEqual(obj1[key], obj2[key])) return false
                    }
                    return true
                },
                /**
                 * renderDeviceList 渲染设备列表( 根据设备数据变化 )
                 * @returns {void}
                */
                renderDeviceList() {
                    $.getJSON('/api/getDevices', data => {
                        if (this.deepEqual(this.reDevices, data) === false) {
                            this.$deviveList.html('')
                            data.devices.forEach(e => {
                                this.$deviveList.append(`<mdui-list-item rounded title="${e.model}" icon="devices" data-id="${e.id}" description="设备ID: ${e.id} 状态: ${e.status}">${e.model}</mdui-list-item>`)
                                if (this.$autoStart[0].checked === true) {
                                    if (e.status === '已连接') 
                                        this.startScrcpy(e.id, e.model, false)
                                }
                            })
                        }
                        this.reDevices = data
                        setTimeout(() => {
                            this.renderDeviceList()
                        }, 2200)
                    })
                },
                updateOption(e) {
                    $.post('/api/updateOption', {key: $(e.target).attr('data-key'), value: e.target.value}, (data) => {
                        if (data.status === false) 
                            console.error(data.err)
                    })
                },
                startScrcpy(deviceId, model, isRecord = false) {
                    $.post('/api/startScrcpy', {deviceId, model, isRecord}, data => {
                        if (data !== 'ok') 
                            this.$snackbar.html(`❌错误: ${data}`)[0].open = true
                    })
                    this.$snackbar.html(`🚀启动中: '${model}' ${isRecord === true ? '屏幕录制' : ''}`)[0].open = true
                }
            },
            startExec() {
                mdui.setColorScheme('#0061a4')
                if (localStorage.theme === 'dark') {
                    mdui.setTheme(localStorage.theme)
                    $('mdui-button-icon.theme').attr('icon', 'bedtime')
                }
                this.renderDeviceList()
                $.contextMenu({
                    selector: ".main .device-list mdui-list-item",
                    trigger: 'right',
                    autoHide: true,
                    items: {
                        "screenControl": {
                            name: "启动scrcpy",
                            callback: function (key, opt) {
                                let model = opt.$trigger.attr('title')
                                let deviceId = opt.$trigger.attr('data-id')
                                global.extendWebPage.startScrcpy(deviceId, model, false)
                            }
                        },
                        "screenRecord": {
                            name: "启动scrcpy&屏幕录制",
                            callback: function (key, opt) {
                                let model = opt.$trigger.attr('title')
                                let deviceId = opt.$trigger.attr('data-id')
                                global.extendWebPage.startScrcpy(deviceId, model, true)
                            }
                        }
                    }
                })
                this.$autoStart[0].checked = localStorage.autoStart === 'true' ? true : false
                this.$autoStart.on('change', e => {
                    localStorage.autoStart = e.target.checked
                })
                let e = {
                    videoBitRate: $('.main .options .video-bit-rate'),
                    maxFps: $('.main .options .max-fps'),
                    maxSize: $('.main .options .max-size'),
                    displayOrientation: $('.main .options .display-orientation'),
                    checkboxs: $('.main .options .checkbox mdui-checkbox'),
                    noDisplay: $('.main .options .checkbox mdui-checkbox[data-key=noDisplay]'),
                    alwaysOnTop: $('.main .options .checkbox mdui-checkbox[data-key=alwaysOnTop]'),
                    turnScreenOff: $('.main .options .checkbox mdui-checkbox[data-key=turnScreenOff]'),
                    windowBorderless: $('.main .options .checkbox mdui-checkbox[data-key=windowBorderless]'),
                    fullScreen: $('.main .options .checkbox mdui-checkbox[data-key=fullScreen]'),
                    stayAwake: $('.main .options .checkbox mdui-checkbox[data-key=stayAwake]'),
                    noAudio: $('.main .options .checkbox mdui-checkbox[data-key=noAudio]'),
                    noVideo: $('.main .options .checkbox mdui-checkbox[data-key=noVideo]')
                }
                e.videoBitRate[0].labelFormatter = (value) => `${value}M`
                $.getJSON('/api/getOptions', data => {
                    for (let key in data) {
                        if (key === 'recordPath') {
                            continue
                        }
                        let inEle = e[key][0]
                        if (inEle.nodeName === 'MDUI-CHECKBOX') 
                            inEle.checked = data[key] === 'on' ? true : false
                        else 
                            inEle.value = data[key]
                    }
                })
                e.videoBitRate.on('change', e => {
                    this.updateOption(e)
                })
                e.maxFps.on('change', e => {
                    this.updateOption(e)
                })
                e.maxSize.on('change', e => {
                    this.updateOption(e)
                })
                e.displayOrientation.on('change', e => {
                    this.updateOption(e)
                })
                e.checkboxs.on('change', e => {
                    $.post('/api/updateOption', {key: $(e.target).attr('data-key'), value: String(e.target.checked).replace('true', 'on').replace('false', 'off')}, (data) => {
                        if (data.status === false) 
                            console.error(data.err)
                    })
                })
            }
        }

        /**
         * urlMatch, URL 通配符模式匹配
         * @param {String} pattern 模式, 支持使用通配符
         * @param {String} url 要匹配的URL
         * @returns {Boolean} 匹配结果
         * */
        function urlMatch(pattern, url) {
            pattern = pattern.replace(/\*/g, '.*?')
            pattern = '^' + pattern + '$'
            const regex = new RegExp(pattern)
            return regex.test(url)
        }

        /**
         * extendWebPage, 扩展Web页面
         * @param {Array} options 配置项
         * @returns {Proxy}
         * */
        function extendWebPage(options) {
            let target = {}
            for (let i = 0; i < options.length; i++) {
                let { routes, style, startExec, bodyExec, loadExec, extend } = options[i]
                Object.assign(target, extend instanceof Object === false ? {} : extend)
                if (typeof routes === 'string')
                    routes = new Array(routes)
                for (let i = 0; i < routes.length; i++) {
                    if (urlMatch(routes[i], location.href)) {
                        $(document.head)
                            .append(`<style>${style}</style>`)
                        if (typeof startExec === 'function')
                            startExec.call(target)
                        if (typeof bodyExec === 'function') {
                            bodyExec = bodyExec.bind(target)
                            $(document)
                                .on('DOMContentLoaded', e.bodyExec)
                        }
                        if (typeof loadExec === 'function') {
                            loadExec = loadExec.bind(target)
                            $(window)
                                .on('load', loadExec)
                        }
                    }
                }
            }
            return new Proxy(target, {
                get(target, key) {
                    return target[key]
                },
                set(target, key, value) {
                    target[key] = value
                }
            })
        }

        global.extendWebPage = extendWebPage([routeLocal])
    })(window)
</script>

</html>